package com.hong.pro.utils;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.XmlUtil;
import com.hong.pro.annotation.Ele;
import com.hong.pro.annotation.Path;
import com.hong.pro.domain.PayQueryRespEntity;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.xpath.XPathConstants;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * @Description:
 * @Author Hong Qiang
 * @Date 2023/9/13 15:15
 * @Version 1.0
 */
public class AidXmlUtil {

    private static String testXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
            "\n" +
            "<data> \n" +
            "<record> \n" +
            "  <returnstatus>Success（成功）</returnstatus>  \n" +
            "  <id>123</id>  \n" +
            "  <lastName>test</lastName>  \n" +
            "  <taskID>885</taskID>  \n" +
            "  <successCounts>1</successCounts> \n" +
            "</record>"+
            "<record> \n" +
            "  <returnstatus>Success（成功）</returnstatus>  \n" +
            "  <id>456</id>  \n" +
            "  <lastName>test002</lastName>  \n" +
            "  <taskID>885</taskID>  \n" +
            "  <successCounts>1</successCounts> \n" +
            "</record> \n"+
            "</data>"
            ;

    private static String ecifxml = "<SERVICE>\n" +
            " <SYS_HEAD>\n" +
            "  <SERVICE_CODE>88888888888</SERVICE_CODE>\n" +
            "  <SERVICE_SCENE>01</SERVICE_SCENE>\n" +
            "  <SOURCE_TYPE>M10</SOURCE_TYPE>\n" +
            "  <ORG_SYS_ID>202040</ORG_SYS_ID>\n" +
            "  <CONSUMER_ID>403010</CONSUMER_ID>\n" +
            "  <PROVIDER_ID>059999</PROVIDER_ID>\n" +
            "  <TRAN_DATE>20230925</TRAN_DATE>\n" +
            "  <TRAN_TIMESTAMP>114855965</TRAN_TIMESTAMP>\n" +
            "  <BRANCH_ID>330200101</BRANCH_ID>\n" +
            "  <USER_ID>002245</USER_ID>\n" +
            "  <CONSUMER_SEQ_NO>40301020230925010038199374</CONSUMER_SEQ_NO>\n" +
            "  <CORP_CODE>9999</CORP_CODE>\n" +
            " </SYS_HEAD>\n" +
            " <APP_HEAD>\n" +
            "  <BUSS_SEQ_NO>20202020230925010000045302</BUSS_SEQ_NO>\n" +
            " </APP_HEAD>\n" +
            " <LOCAL_HEAD>\n" +
            "  </LOCAL_HEAD>\n" +
            " <BODY>\n" +
//            "  <CST_IB_ACS_FIN_LIST_ARRAY>\n" +
            "  <CLIENT_NO>2620582802</CLIENT_NO>\n" +
            "  <MBL_REPEAT_INSP_FLG></MBL_REPEAT_INSP_FLG>\n" +
            "  <CLIENT_TYPE>2</CLIENT_TYPE>\n" +
            "  <CLIENT_NAME>叶昊强企业有限公司</CLIENT_NAME>\n" +
            "  <CSTMR_ENGLSH_NM></CSTMR_ENGLSH_NM>\n" +
            "  <CLNT_CHIN_ABBRVTN></CLNT_CHIN_ABBRVTN>\n" +
            "  <CO_ECNM_TP></CO_ECNM_TP>\n" +
            "  <OTHR_FINNC_ORGNZTN_CD></OTHR_FINNC_ORGNZTN_CD>\n" +
            "  <AST_TOT_AMT></AST_TOT_AMT>\n" +
            "  <GLOBAL_INFO_ARRAY>\n" +
            "   <CERT_ORG_CODE></CERT_ORG_CODE>\n" +
            "   <GLOBAL_ID>9191090718437097</GLOBAL_ID>\n" +
            "   <GLOBAL_MATURE_DATE></GLOBAL_MATURE_DATE>\n" +
            "   <GLOBAL_SIGN_ISSUE_DT></GLOBAL_SIGN_ISSUE_DT>\n" +
            "   <GLOBAL_TYPE>A9900</GLOBAL_TYPE>\n" +
            "   <MAIN_GLOBAL_FLG>1</MAIN_GLOBAL_FLG>\n" +
            "  </GLOBAL_INFO_ARRAY>\n" +
            "  <GLOBAL_INFO_ARRAY>\n" +
            "   <CERT_ORG_CODE></CERT_ORG_CODE>\n" +
            "   <GLOBAL_ID>91910907184aaaaa</GLOBAL_ID>\n" +
            "   <GLOBAL_MATURE_DATE></GLOBAL_MATURE_DATE>\n" +
            "   <GLOBAL_SIGN_ISSUE_DT></GLOBAL_SIGN_ISSUE_DT>\n" +
            "   <GLOBAL_TYPE>A32121</GLOBAL_TYPE>\n" +
            "   <MAIN_GLOBAL_FLG>1</MAIN_GLOBAL_FLG>\n" +
            "  </GLOBAL_INFO_ARRAY>\n" +
//            "  </CST_IB_ACS_FIN_LIST_ARRAY>\n" +
//            "  <CST_IB_ACS_FIN_LIST_ARRAY>\n" +
//            "  <CLIENT_NO>33777777</CLIENT_NO>\n" +
//            "  <MBL_REPEAT_INSP_FLG></MBL_REPEAT_INSP_FLG>\n" +
//            "  <CLIENT_TYPE>2</CLIENT_TYPE>\n" +
//            "  <CLIENT_NAME>XXX企业有限公司</CLIENT_NAME>\n" +
//            "  <CSTMR_ENGLSH_NM></CSTMR_ENGLSH_NM>\n" +
//            "  <CLNT_CHIN_ABBRVTN></CLNT_CHIN_ABBRVTN>\n" +
//            "  <CO_ECNM_TP></CO_ECNM_TP>\n" +
//            "  <OTHR_FINNC_ORGNZTN_CD></OTHR_FINNC_ORGNZTN_CD>\n" +
//            "  <AST_TOT_AMT></AST_TOT_AMT>\n" +
//            "  <GLOBAL_INFO_ARRAY>\n" +
//            "   <CERT_ORG_CODE></CERT_ORG_CODE>\n" +
//            "   <GLOBAL_ID>AA999992222</GLOBAL_ID>\n" +
//            "   <GLOBAL_MATURE_DATE></GLOBAL_MATURE_DATE>\n" +
//            "   <GLOBAL_SIGN_ISSUE_DT></GLOBAL_SIGN_ISSUE_DT>\n" +
//            "   <GLOBAL_TYPE>Bcccc1</GLOBAL_TYPE>\n" +
//            "   <MAIN_GLOBAL_FLG>1</MAIN_GLOBAL_FLG>\n" +
//            "  </GLOBAL_INFO_ARRAY>\n" +
//            "  <GLOBAL_INFO_ARRAY>\n" +
//            "   <CERT_ORG_CODE></CERT_ORG_CODE>\n" +
//            "   <GLOBAL_ID>JK9999111</GLOBAL_ID>\n" +
//            "   <GLOBAL_MATURE_DATE></GLOBAL_MATURE_DATE>\n" +
//            "   <GLOBAL_SIGN_ISSUE_DT></GLOBAL_SIGN_ISSUE_DT>\n" +
//            "   <GLOBAL_TYPE>B3312</GLOBAL_TYPE>\n" +
//            "   <MAIN_GLOBAL_FLG>1</MAIN_GLOBAL_FLG>\n" +
//            "  </GLOBAL_INFO_ARRAY>\n" +
//            "  </CST_IB_ACS_FIN_LIST_ARRAY>\n" +
            " </BODY>\n" +
            "</SERVICE>";

    private static String apxml = "<ap>\n" +
            "\t<SerialNo>20231122094214100015</SerialNo>\n" +
            "\t<RespCode>9999</RespCode>\n" +
            "\t<RespInfo>托管行处理中</RespInfo>\n" +
            "\t<TransCode>TG126</TransCode>\n" +
            "\t<content>\n" +
            "\t\t<CustInsCode>ZLSZDCYQ12023090900041</CustInsCode>\n" +
            "\t\t<InsStatus>INS00</InsStatus>\n" +
            "\t</content>\n" +
            "</ap>";

    public static void main(String[] args) {
//        Institution institution = new Institution();
//        institution.setClient_type("1");
//        institution.setClient_no("123000001");
//        institution.setClient_name("hong");
//        List<LoopDTO> loopDTOS = new ArrayList<>();
//        LoopDTO dto = new LoopDTO();
//        dto.setText1("aaaa");
//        dto.setText2("bbbb");
//        LoopDTO dto2 = new LoopDTO();
//        dto2.setText1("zzzzz");
//        dto2.setText2("xxxxx");
//        loopDTOS.add(dto);
//        loopDTOS.add(dto2);
//        institution.setLoopDTOS(loopDTOS);
//        System.out.println(StringUtils.substringAfter("a.b.c","."));
//        System.out.println(convertBean2Xml(institution,Institution.class));
        PayQueryRespEntity respEntity = AidXmlUtil.readXmlStr3Object(apxml, PayQueryRespEntity.class);
        System.out.println(respEntity.toString());
    }

    /**
     * xml字符串转对象实体
     * @param xml
     * @param c
     * @param <T>
     * @return
     */
    public static <T> T readXmlStrToObject(String xml,Class c){
        Document docResult = XmlUtil.readXML(xml);
        Object object = ReflectUtil.newInstance(c);
        Field[] fields = ReflectUtil.getFields(c);
        Arrays.stream(fields).filter(field->Objects.nonNull(field.getAnnotation(Path.class))).forEach(field->{
            Path p = field.getAnnotation(Path.class);
            if (p.loop()){
                //循环节点
                Class elementClazz = p.element();
                Field[] childField = ReflectUtil.getFields(elementClazz);
                List<Object> elementlist = new ArrayList<>();
                Element parElement = XmlUtil.getElementByXPath(StringUtils.substringBeforeLast(p.value(),"/"), docResult);
                List<Element> elements = XmlUtil.getElements(parElement,StringUtils.substringAfterLast(p.value(),"/"));
                for (Element element:elements){
                    Object elementObject = ReflectUtil.newInstance(elementClazz);
                    Arrays.stream(childField).forEach(ele->{
                        Path ePath = ele.getAnnotation(Path.class);
                        ReflectUtil.setFieldValue(elementObject,ele,XmlUtil.elementText(element,StringUtils.substringAfterLast(ePath.value(),"/")));
                    });
                    elementlist.add(elementObject);
                }
                ReflectUtil.setFieldValue(object,field,elementlist);
            }else {
                Object value = XmlUtil.getByXPath(p.value(), docResult, XPathConstants.STRING);
                if (Objects.nonNull(value)){
                    ReflectUtil.setFieldValue(object,field,value);
                }
            }
        });
        return (T) object;
    }

    public static <T> T readXmlStr3Object(String xml,Class c){
        Document docResult = XmlUtil.readXML(xml);
        Object o = ReflectUtil.newInstance(c);
        Field[] fields = ReflectUtil.getFields(c);
        Arrays.stream(fields).filter(e-> Objects.nonNull(e.getAnnotation(Path.class))).forEach(e->{
            Path p = e.getAnnotation(Path.class);
            if (p.loop()){
                Class elementClazz = p.element();
                Field[] childField = ReflectUtil.getFields(elementClazz);
                List<Object> elementlist = new ArrayList<>();
                Element parElement = XmlUtil.getElementByXPath(StringUtils.substringBeforeLast(p.value(),"/"), docResult);
                List<Element> elements = XmlUtil.getElements(parElement,StringUtils.substringAfterLast(p.value(),"/"));
                for (Element element:elements){
                    Object elementObject = ReflectUtil.newInstance(elementClazz);
                    Arrays.stream(childField).forEach(ele->{
                        Path ePath = ele.getAnnotation(Path.class);
                        ReflectUtil.setFieldValue(elementObject,ele,XmlUtil.elementText(element,StringUtils.substringAfterLast(ePath.value(),"/")));
                    });
                    elementlist.add(elementObject);
                }
                ReflectUtil.setFieldValue(o,e,elementlist);
            }else {
                Object value = XmlUtil.getByXPath(p.value(), docResult, XPathConstants.STRING);
                if (Objects.nonNull(value)){
                    ReflectUtil.setFieldValue(o,e,value);
                }
            }
        });
        return (T) o;
    }

    public static <T> T readXmlStr2Object(String xml,Class c){
        Document docResult = XmlUtil.readXML(xml);
        Object object = ReflectUtil.newInstance(c);
        Path parPath = (Path) c.getAnnotation(Path.class);
        if (Objects.nonNull(parPath)){
            Element element = XmlUtil.getElementByXPath(parPath.value(),docResult);
            object = convertElement2Obj(element,c);
        }
        return (T) object;
    }

    public static <T> List<T> readXmlStrToObjectList(String xml,Class c,String arrPath){
        Document docResult = XmlUtil.readXML(xml);
        List<Object> resList = new ArrayList<>();
        //数组节点的上一节点
        Element parElement = XmlUtil.getElementByXPath(StringUtils.substringBeforeLast(arrPath,"/"), docResult);
        //全部数组节点 <list></list><list></list>
        List<Element> elements = XmlUtil.getElements(parElement,StringUtils.substringAfterLast(arrPath,"/"));
        //遍历所有节点转为对象
        elements.stream().forEach(e->{
            resList.add(convertElement2Obj(e,c));
        });
        return (List<T>) resList;
    }

    public static <T> T convertNode2Obj(Node node,Class c){
        Field[] fields = ReflectUtil.getFields(c);
        Object object = ReflectUtil.newInstance(c);
        Arrays.stream(fields).filter(e->Objects.nonNull(e.getAnnotation(Path.class))).forEach(e->{
            Path p = e.getAnnotation(Path.class);
            ReflectUtil.setFieldValue(object,e,XmlUtil.getByXPath(StringUtils.substringAfterLast(p.value(),"/"),node,XPathConstants.STRING));
        });
        return (T) object;
    }

    /**
     * element转对象
     * @param element
     * @param c
     * @param <T>
     * @return
     */
    public static <T> T convertElement2Obj(Element element,Class c){
        Object object = ReflectUtil.newInstance(c);
        Field[] fields = ReflectUtil.getFields(c);
        Arrays.stream(fields).filter(field->Objects.nonNull(field.getAnnotation(Path.class))).forEach(field -> {
            Path p = field.getAnnotation(Path.class);
            if (p.loop()){
                NodeList nodeList = element.getElementsByTagName(StringUtils.substringAfterLast(p.value(),"/"));
                if (nodeList.getLength()!=0){
                    List<Object> childList = new ArrayList<>();
                    for (int i=0;i<nodeList.getLength();i++){
                        childList.add(convertNode2Obj(nodeList.item(i),p.element()));
                    }
                    ReflectUtil.setFieldValue(object,field,childList);
                }
            }else {
//                ReflectUtil.setFieldValue(object,field,XmlUtil.elementText(element,StringUtils.substringAfterLast(p.value(),"/")));

            }
        });
        return (T) object;
    }

    public static <T> String convertBean2Xml(T t,Class c){
        Ele element = (Ele) c.getAnnotation(Ele.class);
        Assert.notNull(element,"缺少必要的Ele元注解");
        Assert.notNull(element.value(),"缺少报文根节点");
        Document document = XmlUtil.createXml();
        Element root = document.createElement(element.value());
        document.appendChild(root);

        Field[] fields = ReflectUtil.getFields(c);
        Arrays.stream(fields).filter(field->Objects.nonNull(field.getAnnotation(Ele.class))).forEach(field -> {
            Ele ele = field.getAnnotation(Ele.class);
            if (ele.isloop()){
                Element parEle = createDeepElement(document,root,ele.value());
                //创建子节点并写入数据
                List<Object> list = (List<Object>) ReflectUtil.getFieldValue(t,field);
                if (Objects.nonNull(list) && list.size()!=0){
                    list.stream().forEach(o->{
                        parEle.appendChild(convertObject2Element(document,o));
                    });
                }
            }else {
                createElementAndWriteContent(ele.value(),root,document,ReflectUtil.getFieldValue(t,field));
            }
        });
        return XmlUtil.toStr(document);
    }


    /**
     * 		<content>
     * 			<TEXT1>aaaa</TEXT1>
     * 			<TEXT2>bbbb</TEXT2>
     * 		</content>
     * @param t
     * @param document
     * @param <T>
     * @return 返回如上格式的Element
     */
    public static <T> Element convertObject2Element(Document document,T t){
        Ele element = t.getClass().getAnnotation(Ele.class);
        Assert.notNull(element,"缺少必要的Ele元注解");
        Assert.notNull(element.value(),"缺少报文根节点");
        Field[] fields = ReflectUtil.getFields(t.getClass());

        String[] nodeArry = element.value().split("\\.");
        //创建上层元素
        Element parEle = document.createElement(nodeArry[0]);
        for (int i=1;i<nodeArry.length;i++){
            parEle.appendChild(document.createElement(nodeArry[i]));
        }
        //循环将各字段加到上层元素中
        Arrays.stream(fields).filter(field->Objects.nonNull(field.getAnnotation(Ele.class))).forEach(field ->{
            Ele ele = field.getAnnotation(Ele.class);
            Element e = document.createElement(ele.value());
            Object value = ReflectUtil.getFieldValue(t,field);
            parEle.appendChild(e);
            e.setTextContent(value.toString());
        });
        return parEle;
    }

    private static Element createElementAndWriteContent(String path,Element parEle,Document document,Object value){
        Assert.notNull(path,"path为空");
        String name = path.split("\\.")[0];
        Element element = null;
        if (parEle.getElementsByTagName(name).getLength()==0){
            element = document.createElement(name);
            parEle.appendChild(element);
        }
        if (path.split("\\.").length>1){
            return createElementAndWriteContent(StringUtils.substringAfter(path,"."),XmlUtil.getElement(parEle,name),document,value);
        }
        if (Objects.nonNull(value) && Objects.nonNull(element)){
            element.setTextContent(value.toString());
        }
        return parEle;
    }

    private static Element createDeepElement(Document document,Element root,String path){
        String[] nodeArry = path.split("\\.");
        Element parEle = root;
        for (int i=0;i<nodeArry.length;i++){
            if (root.getElementsByTagName(nodeArry[i]).getLength()==0){
                Element e = document.createElement(nodeArry[i]);
                parEle.appendChild(e);
            }
            parEle = XmlUtil.getElement(root,nodeArry[i]);
        }
        return parEle;
    }

}
